package com.atldy.commerce.filter;

import cn.hutool.json.JSONUtil;
import com.atldy.commerce.JWTUtils;
import com.atldy.commerce.enums.CommonConstant;
import com.atldy.commerce.enums.GatewayConstant;
import com.atldy.commerce.vo.JwtToken;
import com.atldy.commerce.vo.LoginUserinfo;
import com.atldy.commerce.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @version 1.0
 * @Author: lidongyang
 * @CreateTime: 2021-08-07 18:54
 * @Description： 登录、注册、鉴权全局过滤器
 */
@Slf4j
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {


    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    /**
     * @author: 唐多令
     * @date: 2021/8/7 19:33
     * @param: [exchange, chain]
     * @description: 登录、注册、鉴权
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求体和响应体
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //登录
        if (request.getURI().getPath().contains(GatewayConstant.LOGIN_URI)) {
            ifLoginOrRegister(GatewayConstant.AUTHORITY_CENTER_TOKEN_URL_FOMAT, request, response);
            return response.setComplete();
        }
        //注册
        if (request.getURI().getPath().contains(GatewayConstant.REGISTER_URI)) {
            ifLoginOrRegister(GatewayConstant.AUTHORITY_CENTER_REGISTER_URL_FOMAT, request, response);
            return response.setComplete();
        }
        //授权 获取用户token
        HttpHeaders httpHeaders = request.getHeaders();
        String token = httpHeaders.getFirst(CommonConstant.JWT_TOKEN_USER_INFO);
        LoginUserinfo loginUserinfo = null;
        try {
            loginUserinfo = JWTUtils.parsingToken(token);
        } catch (Exception e) {
            log.error("授权异常，异常信息为：[{}],[{}]", e.getMessage(), e);
        }
        //获取不到用户信息，返回401
        if (loginUserinfo == null) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //验证通过，放行
        return chain.filter(exchange);
    }

    private void ifLoginOrRegister(String uriFormat, ServerHttpRequest request, ServerHttpResponse response) {
        String token = restLoginOrRegister(uriFormat, request);
        response.getHeaders().add(CommonConstant.JWT_TOKEN_USER_INFO, null == token ? "null" : token);
        response.setStatusCode(HttpStatus.OK);
    }


    /**
     * 根据请求信息进行登录 、注册
     */
    private String restLoginOrRegister(String uriFormat, ServerHttpRequest request) {

        //service-id就是服务实例名称
        ServiceInstance serviceInstance =
                loadBalancerClient.choose(CommonConstant.AUTHORITY_CENTER_SERVICE_ID);
        log.info("serviceInstance get start [{}] , [{}] , [{}] ", serviceInstance.getServiceId(),
                serviceInstance.getInstanceId(), JSONUtil.formatJsonStr(serviceInstance.getMetadata().toString()));
        //HTTP路径
        String requestUrl = String.format
                (uriFormat, serviceInstance.getHost(), serviceInstance.getPort());
        //请求参数
        UsernameAndPassword requestBody =
                JSONUtil.toBean(parseBodyFormCacheRequest(request), UsernameAndPassword.class);
        log.info("请求URL：[{}]，请求参数:[{}]", requestUrl, JSONUtil.formatJsonStr(requestBody.toString()));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        JwtToken token = restTemplate.postForObject(
                requestUrl,
                new HttpEntity<>(JSONUtil.toJsonStr(requestBody), httpHeaders)
                , JwtToken.class);
        if (token != null) {
            return token.getToken();
        }
        return null;
    }


    /**
     * 从缓存中取登录、注册出请求体
     */
    private String parseBodyFormCacheRequest(ServerHttpRequest request) {
        //从缓存取出请求体
        Flux<DataBuffer> body = request.getBody();
        //定义原子引用
        AtomicReference<String> reference = new AtomicReference<>();
        //订阅缓冲区消费请求体
        body.subscribe(dataBuffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
            //一定要使用DataBufferUtils.release 释放掉，否则，会出现内存泄露
            DataBufferUtils.release(dataBuffer);
            reference.set(charBuffer.toString());
        });
        return reference.get();
    }

    /**
     * 过滤器执行的优先级，数值越大，越排后
     */
    @Override
    public int getOrder() {
        // TODO: 2021/8/7 需要在全局请求缓存过滤器之后执行
        return HIGHEST_PRECEDENCE + 2;
    }
}